﻿using NN = NNCore;

namespace Controller
{
    public class PlayerNN : AbstractPlayer
    {
        protected ReversiController reversi;
        protected NN.Controller nnController;

        public PlayerNN(int id, Token token, ReversiController reversi, bool timerEnabled, double timerInterval, NN.Controller nnController)
            : base(id, token, timerEnabled, timerInterval)
        {
            this.reversi = reversi;
            this.nnController = nnController;
        }

        protected override void MakeMove(object sender, System.Timers.ElapsedEventArgs e)
        {
            double maxValue = double.MinValue;
            int index = 0;
            for (int i = 0; i < possibleMoves.Length; i++)
            {
                double value = nnController.Run(UtiliseSymmetrie(GetAsDoubleArray(reversi.GetBoardAfterPlaceToken(possibleMoves[i].Column, possibleMoves[i].Row)), possibleMoves[i].Column, possibleMoves[i].Row));
                if (value > maxValue)
                {
                    index = i;
                    maxValue = value;
                }
            }
            reversi.PlaceToken(possibleMoves[index].Column, possibleMoves[index].Row, this.GetType());
        }

        private double[] UtiliseSymmetrie(double[] tokenDoubleArray, int column, int row)
        {
            double[,] token2Dim = new double[8, 8];
            for (int i = 0; i < 64; i++)
            {
                token2Dim[i / 8, i % 8] = tokenDoubleArray[i];
            }

            if (column < 4 && row < 4)
            {
                // 1. Quadrant
                if (column <= row)
                    token2Dim = RotateMatrixRight(token2Dim);
                else
                    token2Dim = RotateMatrixRight(TransposeMatrix(token2Dim));
            }

            if (column > 3 && row < 4)
            {
                // 2. Quadrant
                if ((row + column) <= 7)
                {
                    // korrekt
                }
                else
                    token2Dim = RotateMatrixRight(TransposeMatrix(RotateMatrixLeft(token2Dim)));
            }

            if (column > 3 && row > 3)
            {
                // 3. Quadrant
                if (column >= row)
                    token2Dim = RotateMatrixLeft(token2Dim);
                else
                    token2Dim = RotateMatrixLeft(TransposeMatrix(token2Dim));
            }

            if (column < 4 && row > 3)
            {
                // 4. Quadrant
                if ((row + column) <= 6)
                    token2Dim = RotateMatrixRight(TransposeMatrix(RotateMatrixRight(token2Dim)));
                else
                    token2Dim = RotateMatrixRight(RotateMatrixRight(token2Dim));
            }

            for (int i = 0; i < 8; i++)
            {
                for (int j = 0; j < 8; j++)
                {
                    tokenDoubleArray[i * 8 + j] = token2Dim[i, j % 8];
                }
            }
            return tokenDoubleArray;
        }

        private double[,] TransposeMatrix(double[,] matrix)
        {
            int n = 8;
            double[,] ret = new double[n, n];

            for (int i = 0; i < n; ++i)
            {
                for (int j = 0; j < n; ++j)
                {
                    ret[i, j] = matrix[j, i];
                }
            }

            return ret;
        }

        public double[,] RotateMatrixRight(double[,] matrix)
        {
            /* W and H are already swapped */
            int w = 8;
            int h = 8;
            double[,] ret = new double[h, w];
            for (int i = 0; i < h; ++i)
            {
                for (int j = 0; j < w; ++j)
                {
                    ret[i, j] = matrix[w - j - 1, i];
                }
            }
            return ret;
        }


        public double[,] RotateMatrixLeft(double[,] matrix)
        {
            /* W and H are already swapped */
            int w = 8;
            int h = 8;
            double[,] ret = new double[h, w];
            for (int i = 0; i < h; ++i)
            {
                for (int j = 0; j < w; ++j)
                {
                    ret[i, j] = matrix[j, h - i - 1];
                }
            }
            return ret;
        }
        private double[] GetAsDoubleArray(Token[,] token)
        {
            double[] ret = new double[token.GetLength(0) * token.GetLength(1)];
            for (int i = 0; i < token.GetLength(0); i++)
            {
                for (int j = 0; j < token.GetLength(1); j++)
                {
                    int index = i * token.GetLength(1) + j;
                    switch (token[i, j])
                    {
                        case Token.Default:
                            ret[index] = 0;
                            break;
                        case Token.Player1:
                            ret[index] = this.token == Token.Player1 ? 1 : -1;
                            break;
                        case Token.Player2:
                            ret[index] = this.token == Token.Player2 ? 1 : -1;
                            break;
                        default:
                            break;
                    }
                }
            }
            return ret;
        }
    }
}
